#include "dag.h"

#include <memory>

using namespace dag;
using namespace std;

Node::Node() 
:	m_CurrentState(NodeState_NotInitialized)
{
}

Node::~Node() throw() {
}

void Node::init(const FrameSpec& expectedInputSpec) {
	_init(expectedInputSpec);
	m_CurrentState = NodeState_Initialized;
}

void Node::learn(const FrameDepot* input, const FrameDepot* output) {
	
	/* validate state of node */
	if (m_CurrentState == NodeState_NotInitialized) {
		throw NodeStateError();
	}
	
	/* validate input arguments */
	switch (traits().resources) {
		case LearnResource_None:
			break;
			
		case LearnResource_Inputs:
			if (input == NULL) {
				throw MissingInputs(this);
			}
			break;
			
		case LearnResource_Outputs:
			if (output == NULL) {
				throw MissingOutputs(this);
			}
			break;
			
		case LearnResource_InputsOutputs:
			if (input == NULL && output == NULL) {
				throw MissingInputsAndOutputs(this);
			}
			else if (input == NULL) {
				throw MissingInputs(this);
			}
			else if (output == NULL) {
				throw MissingOutputs(this);
			}
			break;
	}
	
	_learn(input, output);
	m_CurrentState = NodeState_Taught;
}

const FrameSpec& Node::inputSpec() const {
	if (m_CurrentState == NodeState_NotInitialized) {
		throw NodeStateError();
	}
	
	return _inputSpec();
}

const FrameSpec& Node::outputSpec() const {
	if (m_CurrentState == NodeState_NotInitialized) {
		throw NodeStateError();
	}
	
	return _outputSpec();
}

auto_ptr<FrameOperator> Node::createForwardOp() const {
	if (m_CurrentState != NodeState_Taught) {
		throw NodeStateError();
	}
	
	return _createForwardOp();
}

auto_ptr<FrameOperator> Node::createReverseOp() const {
	if (m_CurrentState != NodeState_Taught) {
		throw NodeStateError();
	}
	
	if (!traits().canReverse) {
		throw NonInvertibleNode(this);
	}
	
	return _createReverseOp();
}

void Node::addListener(NodeProgressListener* listener) {
	if (listener != NULL) {
		m_Listeners.insert(listener);
	}
}
void Node::removeListener(NodeProgressListener* listener) {
	m_Listeners.erase(listener);
}

void Node::updateLearnProgress(const string& msg, double progress) const {
	for (set<NodeProgressListener*>::iterator lIter = m_Listeners.begin();
		 lIter != m_Listeners.end(); lIter++) {
		(*lIter)->progress(msg, progress);
	}
}

void Node::resetNodeState() {
	m_CurrentState = NodeState_NotInitialized;
}

auto_ptr<FrameOperator> Node::_createReverseOp() const {
	throw NonInvertibleNode(this);
}